#ifndef __TERRAIN_CHUNK_H__
#define __TERRAIN_CHUNK_H__

#include "Engine/Point2.hpp"
#include "Engine/Vector2.hpp"
#include "Terrain.h"
#include "GameLock.hpp"

using namespace PocketSurvival::Engine;

namespace PocketSurvival
{

    /*
                 (0xFF000000)                     (0xF000)              (0xFF)
               sun light height                   humidity            top height
                +-------------+                   +-----+          +-------------+
                |             |                   |     |          |             |
    ShaftValue: 0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0      (32 bit)
                                 |             |          |     |
                                 +-------------+          +-----+
                                  bottom height         temperature
                                    (0xFF0000)            (0xF00)
    */

    enum class TerrainChunkState
    {
        NotLoaded,                  // 区块还未加载
        InvalidContents1,           // 加载了内容1
        InvalidContents2,           // 加载了内容2
        InvalidContents3,           // 加载了内容3
        InvalidContents4,           // 加载了内容4
        InvalidLight,               // 加载光源
        InvalidPropagatedLight,     // 光源拓展，加载了关照，然后会直接跳到Valid
        InvalidVertices1,           // 无
        InvalidVertices2,           // 无
        Valid
    };

    typedef union
    {
        struct
        {
            int32_t shafts[256];
            int32_t cells[65536];
        };
        uint8_t terrainBuff[sizeof(cells) + sizeof(shafts)];
    } TerrainData;

    class TerrainChunk
    {
    public:
        TerrainChunk();
        TerrainChunk(int32_t coordX, int32_t coordY);
        void initialize(int32_t coordX, int32_t coordY) noexcept;

        // 对于方块的读取和修改，这里都是有进行加锁处理，可以放心使用
        inline int32_t calculateTopmostCellHeight(int32_t x, int32_t z);
        inline int32_t getCellValueFast(int32_t index) noexcept;
        inline int32_t getCellValueFast(int32_t x, int32_t y, int32_t z) noexcept;
        inline void setCellValueFast(int32_t index, int32_t value) noexcept;
        inline void setCellValueFast(int32_t x, int32_t y, int32_t z, int32_t value) noexcept;
        inline int32_t getCellContentsFast(int32_t x, int32_t y, int32_t z) noexcept;
        inline int32_t getCellLightFast(int32_t x, int32_t y, int32_t z) noexcept;

        // 无锁获取和修改方块，用于密集的修改或者获取计算。需要自行使用 cellValueLock 进行加锁以确保数据安全
        int32_t calculateTopmostCellHeightNoLock(int32_t x, int32_t z);
        inline int32_t getCellValueFastNoLock(int32_t index) noexcept;
        inline int32_t getCellValueFastNoLock(int32_t x, int32_t y, int32_t z) noexcept;
        inline void setCellValueFastNoLock(int32_t index, int32_t value) noexcept;
        inline void setCellValueFastNoLock(int32_t x, int32_t y, int32_t z, int32_t value) noexcept;

        // 由于 轴(Shaft) 数据修改和使用比较少，这里暂时没有使用到锁，也因此可能会出现bug，使用时需要十分小心
        inline int32_t getShaftValueFast(int32_t x, int32_t z) noexcept;
        inline void setShaftValueFast(int32_t x, int32_t z, int32_t value) noexcept;
        inline int32_t getTemperatureFast(int32_t x, int32_t z) noexcept;
        inline void setTemperatureFast(int32_t x, int32_t z, int32_t temperature) noexcept;
        inline int32_t getHumidityFast(int32_t x, int32_t z) noexcept;
        inline void setHumidityFast(int32_t x, int32_t z, int32_t humidity) noexcept;
        inline int32_t getTopHeightFast(int32_t x, int32_t z) noexcept;
        inline void setTopHeightFast(int32_t x, int32_t z, int32_t topHeight) noexcept;
        inline int32_t getBottomHeightFast(int32_t x, int32_t z) noexcept;
        inline void setBottomHeightFast(int32_t x, int32_t z, int32_t bottomHeight) noexcept;
        inline int32_t getSunlightHeightFast(int32_t x, int32_t z) noexcept;
        inline void setSunlightHeightFast(int32_t x, int32_t z, int32_t sunlightHeight) noexcept;

        /**
         * @brief 改变对应位置的旧方块值，这里会加锁处理，并判断旧方块是否一致，一致才会替换，并返回true。
         *        使用场景：熔炉烧完东西的一瞬间，另一个线程处理熔炉破坏，这时候就需要判断熔炉还在不在，在才替换成没燃烧的炉子，不在则不进行方块修改
         * @return true: 成功替换 | false: 旧方块值不一样
         */
        bool changeOldCellValue(int32_t x, int32_t y, int32_t z, int32_t oldValue, int32_t newValue);

    public:
        TerrainData terrainData;
        Point2 coords;
        Point2 origin;
        Vector2 center;
        TerrainChunkState State;

        bool isChange;
        bool isRemove;
        float recycleTime;
        Recursive_SpinLock cellValueLock;
        // std::recursive_mutex cellValueMutex;

    public:
        static inline int32_t CalculateCellIndex(int32_t x, int32_t y, int32_t z)
        {
            return y + x * 256 + z * 256 * 16;
        }
    };

    // 获取轴位置的最高位方块的高度
    inline int32_t TerrainChunk::calculateTopmostCellHeight(int32_t x, int32_t z)
    {
        cellValueLock.lock();
        int32_t height = calculateTopmostCellHeightNoLock(x, z);
        cellValueLock.unlock();
        return height;
    }
    // 获取方块值
    inline int32_t TerrainChunk::getCellValueFast(int32_t index) noexcept
    {
        cellValueLock.lock();
        int32_t cellValue = terrainData.cells[index];
        cellValueLock.unlock();
        return cellValue;
    }
    // 获取对应位置的方块值
    inline int32_t TerrainChunk::getCellValueFast(int32_t x, int32_t y, int32_t z) noexcept
    {
        return getCellValueFast(CalculateCellIndex(x, y, z));
    }
    // 修改方块值
    inline void TerrainChunk::setCellValueFast(int32_t index, int32_t value) noexcept
    {
        cellValueLock.lock();
        terrainData.cells[index] = value;
        cellValueLock.unlock();
    }
    // 修改对应位置的方块值
    inline void TerrainChunk::setCellValueFast(int32_t x, int32_t y, int32_t z, int32_t value) noexcept
    {
        setCellValueFast(CalculateCellIndex(x, y, z), value);
    }
    inline int32_t TerrainChunk::getCellContentsFast(int32_t x, int32_t y, int32_t z) noexcept
    {
        return Terrain::ExtractContents(getCellValueFast(x, y, z));
    }
    inline int32_t TerrainChunk::getCellLightFast(int32_t x, int32_t y, int32_t z) noexcept
    {
        return Terrain::ExtractLight(getCellValueFast(x, y, z));
    }




    inline int32_t TerrainChunk::getCellValueFastNoLock(int32_t index) noexcept
    {
        return terrainData.cells[index];
    }
    inline int32_t TerrainChunk::getCellValueFastNoLock(int32_t x, int32_t y, int32_t z) noexcept
    {
        return terrainData.cells[CalculateCellIndex(x, y, z)];
    }
    inline void TerrainChunk::setCellValueFastNoLock(int32_t index, int32_t value) noexcept
    {
        terrainData.cells[index] = value;
    }
    inline void TerrainChunk::setCellValueFastNoLock(int32_t x, int32_t y, int32_t z, int32_t value) noexcept
    {
        terrainData.cells[CalculateCellIndex(x, y, z)] = value;
    }



    inline int32_t TerrainChunk::getShaftValueFast(int32_t x, int32_t z) noexcept
    {
        return terrainData.shafts[x + z * 16];
    }

    inline void TerrainChunk::setShaftValueFast(int32_t x, int32_t z, int32_t value) noexcept
    {
        terrainData.shafts[x + z * 16] = value;
    }

    inline int32_t TerrainChunk::getTemperatureFast(int32_t x, int32_t z) noexcept
    {
        return Terrain::ExtractTemperature(getShaftValueFast(x, z));
    }

    inline void TerrainChunk::setTemperatureFast(int32_t x, int32_t z, int32_t temperature) noexcept
    {
        setShaftValueFast(x, z, Terrain::ReplaceTemperature(getShaftValueFast(x, z), temperature));
    }

    inline int32_t TerrainChunk::getHumidityFast(int32_t x, int32_t z) noexcept
    {
        return Terrain::ExtractHumidity(getShaftValueFast(x, z));
    }

    inline void TerrainChunk::setHumidityFast(int32_t x, int32_t z, int32_t humidity) noexcept
    {
        setShaftValueFast(x, z, Terrain::ReplaceHumidity(getShaftValueFast(x, z), humidity));
    }

    inline int32_t TerrainChunk::getTopHeightFast(int32_t x, int32_t z) noexcept
    {
        return Terrain::ExtractTopHeight(getShaftValueFast(x, z));
    }

    inline void TerrainChunk::setTopHeightFast(int32_t x, int32_t z, int32_t topHeight) noexcept
    {
        setShaftValueFast(x, z, Terrain::ReplaceTopHeight(getShaftValueFast(x, z), topHeight));
    }

    inline int32_t TerrainChunk::getBottomHeightFast(int32_t x, int32_t z) noexcept
    {
        return Terrain::ExtractBottomHeight(getShaftValueFast(x, z));
    }

    inline void TerrainChunk::setBottomHeightFast(int32_t x, int32_t z, int32_t bottomHeight) noexcept
    {
        setShaftValueFast(x, z, Terrain::ReplaceBottomHeight(getShaftValueFast(x, z), bottomHeight));
    }

    inline int32_t TerrainChunk::getSunlightHeightFast(int32_t x, int32_t z) noexcept
    {
        return Terrain::ExtractSunlightHeight(getShaftValueFast(x, z));
    }

    inline void TerrainChunk::setSunlightHeightFast(int32_t x, int32_t z, int32_t sunlightHeight) noexcept
    {
        setShaftValueFast(x, z, Terrain::ReplaceSunlightHeight(getShaftValueFast(x, z), sunlightHeight));
    }

}

#endif